/**
 * @fileoverview
 * This script defines a widget for interacting with expression data from flyatlas.
 * @author <a href="http://purl.org/net/aliman">Alistair Miles</a>
 * @version $Revision:538 $ on $Date: 2008-08-27 09:08:41 +0100 (Wed, 27 Aug 2008) $ by $Author: aliman $
 * @requires flyui.util
 * @requires flyui.sparql.Service
 * @requires flyui.flyatlas.Service
 * @requires YAHOO.util.Connect
 * @requires jQuery
 * For license terms see http://flyui.googlecode.com
 */
 
flyui.namespace("flyui.flyatlas");
 
 
 
 
 

/*
 * ----------------------------------------------------------------
 *                             WIDGET
 * ----------------------------------------------------------------
 */







flyui.flyatlas.ProfileWidget = function( service, renderer ) {
    var _context = "flyui.flyatlas.ProfileWidget";
    try {
        
        flyui.debug("call private constructor", _context);
        this.__init__(service, renderer);
        
    } catch (unexpected) {
        flyui.debug("rethrowing "+unexpected.name+", "+unexpected.message, _context);
        throw unexpected;    
    }
    
}



 
 



/** @private */
flyui.flyatlas.ProfileWidget.prototype._controller = null;

/**
 * @private
 * @type flyui.mvcutils.GenericModel2
 */ 
flyui.flyatlas.ProfileWidget.prototype._model = null;

/**
 * @private
 */
flyui.flyatlas.ProfileWidget.prototype._renderer = null;

/**
 * @private
 */
flyui.flyatlas.ProfileWidget.prototype._service = null;










/**
 * @private
 */
flyui.flyatlas.ProfileWidget.prototype.__init__ = function( service, renderer ) {
    var _context = "flyui.flyatlas.ProfileWidget.prototype.__init__";
    try {
        
        this._service = service;
        this._renderer = renderer;
 
        this._resultsReceivedEvent = new YAHOO.util.CustomEvent("RESULTSRECEIVED", this);
        
        flyui.debug("create a model", _context);
        this._model = new flyui.mvcutils.GenericModel2();
        this._model.setDefinition(flyui.flyatlas.ProfileWidget.modelDefinition);
        
        flyui.debug("create a controller", _context);
        this._controller = new flyui.flyatlas.ProfileWidget.Controller(this._model, service, this);
        
        flyui.debug("connect the renderer to the model", _context);
        this._renderer.connect(this._model);
        
    } catch (unexpected) {
        flyui.debug("rethrowing "+unexpected.name+", "+unexpected.message, _context);
        throw unexpected;    
    }
};







/**
 * @public
 * @param {Array<String>} querynames
 * @param {Array<String>} flybaseids
 */
flyui.flyatlas.ProfileWidget.prototype.getProbesAndGenesByExpressionProfile = function( profile, limit ) {
    var _context = "flyui.flyatlas.ProfileWidget.prototype.getProbesAndGenesByExpressionProfile";
    try {
        
        flyui.debug("pass through to controller", _context);
        this._controller.getProbesAndGenesByExpressionProfile(profile, limit);
        
    } catch (unexpected) {
        flyui.debug("rethrowing "+unexpected.name+", "+unexpected.message, _context);
        throw unexpected;    
    }
};






/**
 * Subscribe to a custom event.
 * @param {String} type the type of the event
 * @param {Function} listener the callback function
 * @param {Object} obj a custom object, passed back to the callback function 
 */
flyui.flyatlas.ProfileWidget.prototype.subscribe = function(type, listener, obj) {
    var _context = "flyui.flyatlas.ProfileWidget.prototype.subscribe";
    try {
        if (type == "RESULTSRECEIVED") {
            this._resultsReceivedEvent.subscribe(listener, obj);
        }
    } catch (unexpected) {
        flyui.debug("rethrowing "+unexpected.name+", "+unexpected.message, _context);
        throw unexpected;    
    }    
};





/*
 * ----------------------------------------------------------------
 *                             CONTROLLER
 * ----------------------------------------------------------------
 */






/**
 * @class
 */
flyui.flyatlas.ProfileWidget.Controller = function( model, service, controllee ) {
    var _context = "flyui.flyatlas.ProfileWidget.Controller";
    try {
        
        this._model = null;
        this._service = null;
        this._controllee = null;
        
        flyui.debug("call private constructor", _context);
        this.__init__(model, service, controllee);        
        
    } catch (unexpected) {
        flyui.debug("rethrowing "+unexpected.name+", "+unexpected.message, _context);
        throw unexpected;    
    }
};








/**
 * @private
 */
flyui.flyatlas.ProfileWidget.Controller.prototype.__init__ = function( model, service, controllee ) {
    var _context = "flyui.flyatlas.ProfileWidget.Controller.prototype.__init__";
    try {
        
        this._model = model;
        this._service = service;
        this._controllee = controllee;
        
    } catch (unexpected) {
        flyui.debug("rethrowing "+unexpected.name+", "+unexpected.message, _context);
        throw unexpected;    
    }
};








/**
 * @public
 */
flyui.flyatlas.ProfileWidget.Controller.prototype.getProbesAndGenesByExpressionProfile = function( profile, limit ) {
    var _context = "flyui.flyatlas.ProfileWidget.Controller.prototype.findGenesByAnyNameBatch";
    try {
        
        flyui.info("getProbesAndGenesByExpressionProfile: "+profile, _context);
        flyui.debug("pass through to private implementation", _context);
        this._getProbesAndGenesByExpressionProfile(profile, this._getProbesAndGenesByExpressionProfileSuccess(), this._getProbesAndGenesByExpressionProfileFailure(), limit);
        
    } catch (unexpected) {
        flyui.debug("rethrowing "+unexpected.name+", "+unexpected.message, _context);
        throw unexpected;    
    }
};








/**
 * @private
 */
flyui.flyatlas.ProfileWidget.Controller.prototype._getProbesAndGenesByExpressionProfile = function( profile, success, failure, limit ) {
    var _context = "flyui.flyatlas.ProfileWidget.Controller.prototype._getProbesAndGenesByExpressionProfile";
    try {
        
        flyui.debug("set state pending", _context);
        this._model.set("STATE", "PENDING");
        
        flyui.debug("set model property query", _context);
        this._model.set("QUERY", profile);

        flyui.debug("set result null", _context);
        this._model.set("RESULTS", null);
                
        flyui.debug("set message null", _context);
        this._model.set("MESSAGE", null);
                
        flyui.debug("set limit "+limit, _context);
        this._model.set("LIMIT", limit);
                
        flyui.debug("kick off request", _context);
        this._service.getProbesAndGenesByExpressionProfile(profile, success, failure, limit);        
        
    } catch (unexpected) {
        flyui.debug("rethrowing "+unexpected.name+", "+unexpected.message, _context);
        throw unexpected;    
    }
};









/**
 * @private
 */
flyui.flyatlas.ProfileWidget.Controller.prototype._getProbesAndGenesByExpressionProfileSuccess = function() {
    var _context = "flyui.flyatlas.ProfileWidget.Controller.prototype._getProbesAndGenesByExpressionProfileSuccess";
    var self = this;
    /**
     * @param {Object<String,<Array<AssayGroup>>} results a map from flybaseids to arrays of AssayGroup objects
     */
    return function( results ) {
        try {
            
            flyui.info("request success", _context);
            
            flyui.debug("set model results", _context);
            self._model.set("RESULTS", results);
            
            flyui.debug("set model state", _context);
            self._model.set("STATE", "READY");
            
            var limit = self._model.get("LIMIT");
            
            flyui.debug("limit "+limit+" results.length "+results.length, _context);
            
            if (results.length == limit) {
                var explanation = "At most " + limit + " results are shown.";
                self._model.set("MESSAGE", explanation);
            }
            
            flyui.debug("fire event", _context);
            self._controllee._resultsReceivedEvent.fire(results);
            
        } catch (unexpected) {
            flyui.debug("rethrowing "+unexpected.name+", "+unexpected.message, _context);
            throw unexpected;    
        }
    };
};







/**
 * @private
 */
flyui.flyatlas.ProfileWidget.Controller.prototype._getProbesAndGenesByExpressionProfileFailure = function() {
    var _context = "flyui.flyatlas.ProfileWidget.Controller.prototype._getProbesAndGenesByExpressionProfileFailure";
    var self = this;
    /**
     * @param {ResponseObject} response
     */
    return function( response ) {
        try {
            
            flyui.err("request failed: "+response.status+" "+response.statusText, _context);
            self._model.set("MESSAGE", "there was an error retrieving data from the server, see the logs for more info");
            self._model.set("STATE", "SERVERERROR");
            
        } catch (unexpected) {
            flyui.debug("rethrowing "+unexpected.name+", "+unexpected.message, _context);
            throw unexpected;    
        }
    };
};







/*
 * ----------------------------------------------------------------
 *                             MODEL DEFINITION
 * ----------------------------------------------------------------
 */





/** 
 * @type Object
 */
flyui.flyatlas.ProfileWidget.modelDefinition = {

    properties : [ "STATE", "RESULTS", "MESSAGE", "QUERY", "LIMIT" ],
    
    values : {
        "STATE" : [ "READY", "PENDING", "SERVERERROR", "UNEXPECTEDERROR" ]
    },
    
    initialize : function( data ) {
        data["STATE"] = "READY";
        data["RESULTS"] = null;
        data["MESSAGE"] = null;
        data["QUERY"] = null;
        data["LIMIT"] = 200;
    }

};







/*
 * ----------------------------------------------------------------
 *                             DEFAULT RENDERER
 * ----------------------------------------------------------------
 */







flyui.flyatlas.ProfileWidget.DefaultRenderer = function() {};


/** 
 * @private 
 * @type Element
 */
flyui.flyatlas.ProfileWidget.DefaultRenderer.prototype._canvas = null;

/**
 * @private 
 * @type Element
 */
flyui.flyatlas.ProfileWidget.DefaultRenderer.prototype._pendingPane = null;

/**
 * @private 
 * @type Element
 */
flyui.flyatlas.ProfileWidget.DefaultRenderer.prototype._resultsSummaryPane = null;

/**
 * @private 
 * @type Element
 */
flyui.flyatlas.ProfileWidget.DefaultRenderer.prototype._resultsPane = null;

/**
 * @private 
 * @type Element
 */
flyui.flyatlas.ProfileWidget.DefaultRenderer.prototype._messagePane = null;





/**
 * Set the DOM element to which this renderer applies and initialise it.
 * @param {Element} canvas
 */
flyui.flyatlas.ProfileWidget.DefaultRenderer.prototype.setCanvas = function( canvas ) {
    var _context = "flyui.flyatlas.ProfileWidget.DefaultRenderer.prototype.setCanvas";
    try {
        
        flyui.debug("set canvas", _context);
        this._canvas = $(canvas);
        this._initCanvas();
        
        
    } catch (unexpected) {
        flyui.debug("rethrowing "+unexpected.name+", "+unexpected.message, _context);
        throw unexpected;    
    }
};






/**
 * @private
 * @param {Element} canvas
 */
flyui.flyatlas.ProfileWidget.DefaultRenderer.prototype._initCanvas = function() {
    var _context = "flyui.flyatlas.ProfileWidget.DefaultRenderer.prototype._initCanvas";
    try {
        
        flyui.debug("begin init canvas", _context);
        
        var canvas = this._canvas;
        canvas.addClass("flyatlasProfileWidget");

        var pp = $("<p class='pendingPane'>pending (please wait, this can take a while) ...</p>").hide();
        canvas.append(pp);
        this._pendingPane = pp;

        var rsp = $("<p class='resultsSummaryPane'>this text should never be displayed</p>").hide();
        canvas.append(rsp);
        this._resultsSummaryPane = rsp;
        
        var rp = $("<div class='resultsPane'></div>").hide();
        canvas.append(rp);
        this._resultsPane = rp;
        
        var mp = $("<p class='messagePane'>this text should never be displayed</p>").hide();
        canvas.append(mp);
        this._messagePane = mp;            

        flyui.debug("init canvas done", _context);
        
    } catch (unexpected) {
        flyui.debug("rethrowing "+unexpected.name+", "+unexpected.message, _context);
        throw unexpected;    
    }
};





/**
 * @public
 */
flyui.flyatlas.ProfileWidget.DefaultRenderer.prototype.connect = function( model ) {
    var _context = "flyui.flyatlas.ProfileWidget.DefaultRenderer.prototype.connect";
    try {
        
        flyui.debug("connect to model", _context);
        model.subscribeAll(this._onModelChanged, this);
        
    } catch (unexpected) {
        flyui.debug("rethrowing "+unexpected.name+", "+unexpected.message, _context);
        throw unexpected;    
    }
};





/**
 * @private
 */
flyui.flyatlas.ProfileWidget.DefaultRenderer.prototype._onModelChanged = function( type, args, self ) {
    var _context = "flyui.flyatlas.ProfileWidget.DefaultRenderer.prototype._onModelChanged";
    try {
        
        var handlers = {
            "STATE":"_onStateChanged",
            "QUERY":"_onQueryChanged",
            "RESULTS":"_onResultsChanged",
            "MESSAGE":"_onMessageChanged"
        };
        var handler = handlers[type];
        flyui.debug("call model changed handler: "+handler, _context);
        self[handler](args[0], args[1], args[2]);
        
    } catch (unexpected) {
        flyui.debug("rethrowing "+unexpected.name+", "+unexpected.message, _context);
        throw unexpected;    
    }
};






/**
 * @private
 */
flyui.flyatlas.ProfileWidget.DefaultRenderer.prototype._onQueryChanged = function( from, to, get ) {
    var _context = "flyui.flyatlas.ProfileWidget.DefaultRenderer.prototype._onQueryChanged";
    try {
        
        // do nothing, we will access the value later
        flyui.debug("query changed from "+from+" to "+to, _context);
        
    } catch (unexpected) {
        flyui.debug("rethrowing "+unexpected.name+", "+unexpected.message, _context);
        throw unexpected;    
    }
};






/**
 * @private
 */
flyui.flyatlas.ProfileWidget.DefaultRenderer.prototype._onStateChanged = function( from, to, get ) {
    var _context = "flyui.flyatlas.ProfileWidget.DefaultRenderer.prototype._onStateChanged";
    try {
        
        flyui.debug("state changed from "+from+" to "+to, _context);

        if ( to == "PENDING" ) {
            this._pendingPane.show();
            this._messagePane.hide();
            this._resultsSummaryPane.hide();
            this._resultsPane.hide();
        }
        else if ( to == "READY" ) {
            this._pendingPane.hide();
            this._messagePane.show();
            this._resultsSummaryPane.show();
            this._resultsPane.show();         
        } 
        else if ( to == "SERVERERROR" || to == "UNEXPECTEDERROR" ) {
            this._pendingPane.hide();
            this._messagePane.show();
            this._resultsSummaryPane.hide();
            this._resultsPane.hide();         
        } 
        else {
            // this should never happen
            throw {message:"invalid state: "+to};
        }
        
    } catch (unexpected) {
        flyui.debug("rethrowing "+unexpected.name+", "+unexpected.message, _context);
        throw unexpected;    
    }
};






/**
 * @private
 */
flyui.flyatlas.ProfileWidget.DefaultRenderer.prototype._onResultsChanged = function( from, to, get ) {
    var _context = "flyui.flyatlas.ProfileWidget.DefaultRenderer.prototype._onResultsChanged";
    try {
        
        flyui.debug("results changed to: "+to, _context);
        
        if (to == null) {
            flyui.debug("empty results summary pane", _context);
            this._resultsSummaryPane.empty();
            flyui.debug("empty results pane", _context);
            this._resultsPane.empty();
        } 
        
        else {

            flyui.debug("render results summary", _context);
            this._renderResultsSummary(to);
    
            flyui.debug("render results", _context);
            this._renderResults(to);        
            
        }
        
    } catch (unexpected) {
        flyui.debug("rethrowing "+unexpected.name+", "+unexpected.message, _context);
        throw unexpected;    
    }
};







/**
 * @private
 */
flyui.flyatlas.ProfileWidget.DefaultRenderer.prototype._onMessageChanged = function( from, to, get ) {
    var _context = "flyui.flyatlas.ProfileWidget.DefaultRenderer.prototype._onMessageChanged";
    try {
        
        flyui.debug("message changed to: "+to, _context);
        
        if (to == null) {
            flyui.debug("empty messsage pane", _context);
            this._messagePane.empty();
        } 
        
        else {

            this._messagePane.html(to);
            
        }
        
    } catch (unexpected) {
        flyui.debug("rethrowing "+unexpected.name+", "+unexpected.message, _context);
        throw unexpected;    
    }
};









/**
 * @private
 * @param {Object<String,<Array<AssayGroup>>} results a map from flybaseids to arrays of assay group objects
 */
flyui.flyatlas.ProfileWidget.DefaultRenderer.prototype._renderResultsSummary = function( results ) {
    var _context = "flyui.flyatlas.ProfileWidget.DefaultRenderer.prototype._renderResultsSummary";
    try {
        
        flyui.debug("render results summary", _context);

        var content = "found "+results.pairs.length+" results ("+results.probes.length+" distinct probe IDs, "+results.genes.length+" distinct gene IDs) from <a href='http://flyatlas.org'>flyatlas.org</a> ("+flyui.flyatlas.provenance+") ...";

        this._resultsSummaryPane.html(content);
        
    } catch (unexpected) {
        flyui.debug("rethrowing "+unexpected.name+", "+unexpected.message, _context);
        throw unexpected;    
    }
};








/**
 * @private
 */
flyui.flyatlas.ProfileWidget.DefaultRenderer.prototype._renderResults = function( results ) {
    var _context = "flyui.flyatlas.ProfileWidget.DefaultRenderer.prototype._renderResults";
    try {
        flyui.debug("render results", _context);
        
        var content = "<strong>matching probes: </strong>";

        for (var i=0; i<results.probes.length;i++) {
            var probe = results.probes[i];
            content += "<strong><a href='http://flyatlas.org/probeset.cgi?name="+probe+"'>"+probe+"</a></strong> (";
            var genecount = 0;
            for (var j=0; j<results.pairs.length; j++) {
                var pair = results.pairs[j];
                if (pair[0] == probe) {
                    if (genecount > 0) {
                        content += ", ";
                    }
                    content += "<a href='http://flybase.org/reports/" + pair[1] + ".html'>" + pair[1] + "</a>";
                    genecount++;
                }
            }
            content += "); ";
        }        
        
        this._resultsPane.html(content);
        
    } catch (unexpected) {
        flyui.debug("rethrowing "+unexpected.name+", "+unexpected.message, _context);
        throw unexpected;    
    }
};











